﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Timers;
using System.Threading.Tasks;
using System.Collections;
using Interfaces;

namespace Scheduling
{
    public class Scheduler : IScheduler
    {
        public event Action<ICommand> Tripped;
        Timer timer;
        private object syncObject = new object();
        private List<IOperation> operations;
        private ISystemConfiguration sysConfig;

        public Scheduler(double period, ISystemConfiguration sysConfig, List<IOperation> operations = null)
        {
            this.sysConfig = sysConfig;
            this.operations = operations == null ? new List<IOperation>() : operations;
            timer = new Timer(period)
            {
                AutoReset = true,
                Enabled = true
            };
            timer.Elapsed += timer_Elapsed;
            timer.Start();
        }

        void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            ExecuteOps();
        }

        public void LoadOperations(List<IOperation> operations)
        {
            lock (syncObject)
            {
                if (operations != null)
                {
                    this.operations = operations;
                }
            }
        }

        public void ExecuteOps()
        {
            lock (syncObject)
            {
                DateTime now = DateTime.Now;
                foreach (IOperation op in operations)
                {
                    if (
                        (op.Years == null || op.Years.Evaluate(now.Year)) &&
                        (op.Months == null || op.Months.Evaluate(now.Month)) &&
                        (op.DaysOfYear == null || op.DaysOfYear.Evaluate(now.DayOfYear)) &&
                        (op.DaysOfMonth == null || op.DaysOfMonth.Evaluate(now.Day)) &&
                        (op.DaysOfWeek == null || op.DaysOfWeek.Evaluate((int)now.DayOfWeek)) &&
                        (op.Hours == null || op.Hours.Evaluate(now.Hour)) &&
                        (op.Minutes == null || op.Minutes.Evaluate(now.Minute))
                        )
                    {
                        foreach (ICommand cmd in op.Commands)
                        {
                            if (Tripped != null)
                            {
                                Tripped(cmd);
                            }
                        }
                    }
                }
            }
        }

        public List<IOperation> GetOperations()
        {
            lock (syncObject)
            {
                return operations;
            }
        }

        public bool Add(IOperation operation)
        {
            lock (syncObject)
            {
                bool retval = false;
                if (operations == null)
                {
                    operations = new List<IOperation>();
                }
                try
                {
                    if (operation != null && !operations.Select(x => x.Name).ToList().Contains(operation.Name))
                    {
                        operations.Add(operation);
                        sysConfig.PersistOperation(operation);
                        retval = true;
                    }
                }
                catch
                {
                    retval = false;
                }
                return retval;
            }
        }

        public bool Add(List<IOperation> ops)
        {
            lock (syncObject)
            {
                bool retval = false;
                if (ops != null && ops.Count() > 0)
                {
                    IEnumerable<IOperation> validOps = ops.Where(x => operations.Select(o => o.Name).Contains(x.Name) == false);
                    operations.AddRange(validOps);
                    sysConfig.PersistOperations(validOps.ToList());
                    retval = true;
                }
                return retval;
            }
        }

        public bool DeleteOperation(string name)
        {
            lock (syncObject)
            {
                bool opDeleted = false;
                if (string.IsNullOrEmpty(name) == false)
                {
                    if (operations.RemoveAll(x => x.Name == name) > 0)
                    {
                        opDeleted = true;
                    }
                    sysConfig.DeleteAllAndPersistOperations(operations); 
                }
                return opDeleted;
            }
        }

        public bool DeleteOperations(List<string> names)
        {
            lock (syncObject)
            {
                bool opsDeleted = false;
                if (names != null && names.Count() != 0)
                {
                    if (operations.RemoveAll(x => names.Contains(x.Name)) > 0)
                    {
                        opsDeleted = true;
                    }
                    sysConfig.DeleteAllAndPersistOperations(operations); 
                }
                return opsDeleted;
            }
        }

        public bool DeleteOperations()
        {
            lock (syncObject)
            {
                operations.Clear();
                sysConfig.DeleteAllAndPersistOperations(operations);
                return true;
            }
        }
    }
}